package com.jrx.anytxn.customer.service.impl;

import com.alibaba.fastjson.JSON;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.StringUtils;
import com.jrx.anytxn.customer.constant.CustomerConstant;
import com.jrx.anytxn.customer.constant.ErrorMsgConstant;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfoQueryReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfoQueryRes;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfoRes;
import com.jrx.anytxn.customer.dto.customer.CustomerRes;
import com.jrx.anytxn.customer.dto.customer.CustomerSecondLevelRes;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerLimitFrozeBaseRes;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerLimitFrozeDetailResp;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerLimitFrozeFrozeReq;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerLimitFrozeInfoReq;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerLimitFrozeQueryRes;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerLimitFrozeRes;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerLimitFrozeResultReq;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerLimitFrozeUseRes;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerLimitVerifyDto;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerUseLimitFrozeFrozeReq;
import com.jrx.anytxn.customer.entity.CmCustomerLimitFroze;
import com.jrx.anytxn.customer.entity.CmCustomerLimitInfo;
import com.jrx.anytxn.customer.entity.CmLimitFrozeUseLog;
import com.jrx.anytxn.customer.handel.ICustomerLimitInfoHandler;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerLimitFrozeMapper;
import com.jrx.anytxn.customer.service.ICmCustomerLimitFrozeService;
import com.jrx.anytxn.customer.service.ICustomerLimitInfoService;
import com.jrx.anytxn.customer.service.ICustomerService;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.entity.PrChannelTable;
import com.jrx.anytxn.param.entity.PrOrganizationTable;
import com.jrx.anytxn.param.service.system.IChannelTableService;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.redisson.service.RedissonLockService;
import com.jrx.anytxn.redisson.util.LockType;
import org.apache.commons.collections.CollectionUtils;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class ICmCustomerLimitFrozeServiceImpl implements ICmCustomerLimitFrozeService {

    private static Logger logger = LoggerFactory.getLogger(ICmCustomerLimitFrozeServiceImpl.class);

    @Autowired
    private ICustomerLimitInfoService customerLimitInfoService;

    @Resource
    private ExtCmCustomerLimitFrozeMapper extCmCustomerLimitFrozeMapper;

    @Autowired
    private ICustomerService customerService;

    @Autowired
    private IChannelTableService channelTableService;

    @Autowired
    private ICustomerLimitInfoHandler customerLimitInfoHandler;

    @Autowired
    private IOrganizationTableService organizationTableService;

    @Autowired
    private ISegmentService segmentService;

    @Autowired
    private RedissonLockService redissonLockService;

    @Autowired
    private CmLimitFrozeUseLogServiceImpl cmLimitFrozeUseLogService;

    @Autowired
    private RedisTemplate<String, Object> template;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public int limitFreezeInfoInsert(CmCustomerLimitFroze cmCustomerLimitFroze) throws TxnException {
        logger.info("creditMedicalFreeze|冻结处理|参数|req：{}", JSON.toJSONString(cmCustomerLimitFroze));
        int result = extCmCustomerLimitFrozeMapper.insertSelective(cmCustomerLimitFroze);
        if (result < 1) {
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), "信用就医额度冻结信息落地失败");
        }
        logger.info("creditMedicalFreeze|冻结处理|结果|result：{}", result);
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int creditMedicalUpdateByParams(CmCustomerLimitFroze limitToUnfreeze) throws TxnBizException {
        logger.info("creditMedicalUnfreeze|解冻处理|参数|limitToUnfreeze：{}", JSON.toJSONString(limitToUnfreeze));
        int updateResult = extCmCustomerLimitFrozeMapper.updateByParams(limitToUnfreeze);
        if (updateResult < 1) {
            String message = MessageFormat.format("userId:{0},客户号：{1},产品号：{2},冻结流水号:{3},信用就医额度解冻失败",
                    limitToUnfreeze.getUserId(), limitToUnfreeze.getCustomerId(), limitToUnfreeze.getProductId(), limitToUnfreeze.getFrozeNo());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
        logger.info("creditMedicalUnfreeze|解冻处理|结果|result：{}", JSON.toJSONString(updateResult));
        return updateResult;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void creditMedicalVerifyLimit(CustomerLimitVerifyDto dto) throws TxnBizException {
        logger.info("creditMedicalVerifyLimit|参数|dto:{}; businessDate :{}",
                JSON.toJSONString(dto), DateUtils.format(dto.getBusinessDate(), DateUtils.DATETIME_PATTERN));
        dto.check();
        BigDecimal frozeAmount = dto.getAmtToUse();
        frozeAmount = frozeAmount == null ? BigDecimal.ZERO : frozeAmount;
        BigDecimal availCreditLimit = dto.getAvailCreditLimit();
        CmCustomerLimitInfo customerLimitInfo = dto.getCustomerLimitInfo();

        //调用客户额度信息校验服务
        customerLimitInfoService.verifyCustomer(customerLimitInfo, dto.getAmtToUse(), dto.getBusinessDate());
        //获取额度冻结信息
        CmCustomerLimitFroze params = getCmCustomerLimitFroze(dto.getTenantId(), dto.getCustomerId(), dto.getProductId(), CustomerConstant.OPERATE_TYPE_F, null);
        List<CmCustomerLimitFroze> cmCustomerFrozeLimits = this.limitFrozeQuery(params);
        BigDecimal totalFrozeAmount = BigDecimal.ZERO;
        if (CollectionUtils.isNotEmpty(cmCustomerFrozeLimits)) {
            totalFrozeAmount = cmCustomerFrozeLimits.stream().map(cmCustomerLimitFroze -> {
                return cmCustomerLimitFroze.getFrozeAmount().subtract(cmCustomerLimitFroze.getUsedAmount());
            }).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, BigDecimal.ROUND_HALF_UP);
        }
        //判断待冻结额度是否大于(可用额度-所有已冻结的金额和)
        BigDecimal availableFreezeAmt = availCreditLimit.subtract(totalFrozeAmount);
        if (frozeAmount.compareTo(availableFreezeAmt) > 0) {
            //错误信息
            String errMsg = String.format(ErrorMsgConstant.CREDIT_MEDICAL_CUSTOMER_LIMIT_FROZE_ERROR, frozeAmount.toString(), dto.getCustomerId(), dto.getProductId(), customerLimitInfo.getLimitId(), availableFreezeAmt.toString());
            //可用额度不够抛出异常
            logger.error(errMsg);
            throw new TxnBizException(errMsg);
        }
        logger.info("creditMedicalVerifyLimit|seq:{}; 校验处理结束|校验通过", dto.getSeqNo());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<CmCustomerLimitFroze> limitFrozeQuery(CmCustomerLimitFroze record) {
        return extCmCustomerLimitFrozeMapper.selectByParams(record);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CustomerLimitFrozeRes creditMedicalFreeze(CustomerLimitFrozeFrozeReq req) throws TxnException, InterruptedException {
        logger.info("creditMedicalFreeze|冻结处理|参数|req：{}", JSON.toJSONString(req));

        CustomerRes customer = customerService.getCustomerByChannelAndUserId(req.getChannel(),
                req.getUserId(), req.getTenantId());
        CustomerSecondLevelRes secondLevel = customer.getSecondLevel();
        // 校验客户是否存在逾期管制
        if (StringUtils.isNotBlank(customer.getFirstLevel().getBlockCode())) {
            String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},冻结处理失败，客户存在逾期管制，管制码为{3};",
                    req.getUserId(), req.getProductId(), req.getFrozeNo(), customer.getFirstLevel().getBlockCode());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), "客户存在逾期管制");
        }
        // 校验客户是否存在逾期管制
        String idExpireDateStr = customer.getFirstLevel().getIdExpireDate();
        LocalDate nowDateAddOne = LocalDate.now().plusDays(1L);
        LocalDate idExpireDate = LocalDate.parse(idExpireDateStr, DateTimeFormatter.ISO_LOCAL_DATE);
        //证件有效期校验 证件将在一天后失效不能做额度冻结
        if (!(idExpireDate.compareTo(nowDateAddOne) > 0)) {
            String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},冻结处理失败，客户身份证有效期在：{3}日之后有效才可做冻额处理;",
                    req.getUserId(), req.getProductId(), req.getFrozeNo(), nowDateAddOne.format(DateTimeFormatter.ISO_LOCAL_DATE));
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
        //3.查询渠道信息判断渠道是否存在
        logger.debug("1.查询渠道信息判断渠道是否存在,渠道：{}", req.getChannel());
        PrChannelTable channel = channelTableService.getChannelTableByChannelIdAndStatus(Constant.YES_FLAG,
                req.getChannel(), req.getTenantId());

        //获取业务日期
        PrOrganizationTable org = organizationTableService.findByOrId(channel.getOrganizationId(), req.getTenantId());

        //返回信息
        CustomerLimitFrozeRes frozeRes = new CustomerLimitFrozeRes();
        String lockKey = req.getProductId() + secondLevel.getCustomerId() + req.getFrozeNo();
        RLock lock = redissonLockService.getRLock(lockKey, LockType.REENTRANT_LOCK);
        try {
            boolean lockFlag = lock.tryLock(CustomerConstant.LOAN_REDIS_LOCK_WAITE_TIME, CustomerConstant.LOAN_REDIS_LOCK_LESS_TIME, TimeUnit.SECONDS);
            if (!lockFlag) {
                String msg = String.format(Constant.GET_REDIS_LOCK_ERROR, lockKey);
                throw new TxnBizException(TxnRespCode.ERROR.getCode(), msg);
            }
            //获取额度信息  客户-产品额度
            CreditInfoRes creditInfoRes = getCustomerCreditLimitInfo(req.getProductId(), req.getSeqNo(), req.getUserId(), secondLevel.getCustomerId(), req.getChannel(), req.getTenantId());
            Date expiryDate = creditInfoRes.getExpiryDate();
            LocalDate expireLocalDate = DateUtils.dateConvertToLocalDate(expiryDate);
            if (!(expireLocalDate.compareTo(nowDateAddOne) > 0)) {
                String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},冻结处理失败，额度失效日期：{3},客户产品对应的额度在:{4}日之后有效才可做额度冻结;",
                        req.getUserId(), req.getProductId(), req.getFrozeNo(), expireLocalDate.format(DateTimeFormatter.ISO_LOCAL_DATE), nowDateAddOne.format(DateTimeFormatter.ISO_LOCAL_DATE));
                logger.error(message);
                throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
            }

            //获取额度冻结信息
            CmCustomerLimitFroze queryParams = getCmCustomerLimitFroze(req.getTenantId(), secondLevel.getCustomerId(), req.getProductId(), null, req.getFrozeNo());
            List<CmCustomerLimitFroze> frozeLimits = this.limitFrozeQuery(queryParams);
            if (CollectionUtils.isNotEmpty(frozeLimits)) {
                String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},冻结处理失败，冻结流水号已存在",
                        req.getUserId(), req.getProductId(), req.getFrozeNo());
                logger.error(message);
                throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
            }

            //信用就医额度校验处理
            CustomerLimitVerifyDto customerLimitVerifyDto = new CustomerLimitVerifyDto();
            customerLimitVerifyDto.setCustomerLimitInfo(creditInfoRes.getCmCustomerLimitInfo());
            customerLimitVerifyDto.setCustomerId(secondLevel.getCustomerId());
            customerLimitVerifyDto.setAvailCreditLimit(creditInfoRes.getAvailCreditLimit());
            customerLimitVerifyDto.setBusinessDate(org.getNextProcessingDate());
            customerLimitVerifyDto.setSeqNo(req.getSeqNo());
            customerLimitVerifyDto.setTenantId(req.getTenantId());
            customerLimitVerifyDto.setProductId(req.getProductId());
            customerLimitVerifyDto.setAmtToUse(req.getFrozeAmount());
            this.creditMedicalVerifyLimit(customerLimitVerifyDto);

            CmCustomerLimitFroze cmCustomerLimitFroze = generateCustomerLimitFroze(req, secondLevel, creditInfoRes, channel.getOrganizationId());
            //额度冻结处理落地
            this.limitFreezeInfoInsert(cmCustomerLimitFroze);

            frozeRes.setSeqNo(req.getSeqNo());
            frozeRes.setUserId(req.getUserId());
            frozeRes.setFrozeNo(req.getFrozeNo());
            frozeRes.setChannel(req.getChannel());
            frozeRes.setProductId(req.getProductId());
            frozeRes.setStatus(cmCustomerLimitFroze.getStatus());
            frozeRes.setResult(CustomerConstant.SUCCESS);

        } catch (DuplicateKeyException e) {
            String message = MessageFormat.format("用户号userId：{0},产品号：{1},冻结流水号：{2},冻结处理失败，冻结流水号已存在",
                    req.getUserId(), req.getProductId(), req.getFrozeNo());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        } finally {
            if (lock.isLocked()) {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }

        logger.info("creditMedicalFreeze|冻结处理|结果|res：{}", JSON.toJSONString(req));

        return frozeRes;
    }

    private CmCustomerLimitFroze generateCustomerLimitFroze(CustomerLimitFrozeFrozeReq req, CustomerSecondLevelRes secondLevel, CreditInfoRes creditInfoRes,
                                                            String organizationId) throws TxnException {
        CmCustomerLimitFroze frozeRecord = new CmCustomerLimitFroze();

        try {
            frozeRecord.setId(segmentService.getId(Constant.CM_CUSTOMER_LIMIT_FROZE));
        } catch (SegmentException e) {
            logger.error("主键生成错误", e);
            throw new TxnException(com.jrx.anytxn.param.constant.ErrorMsgConstant.ERROR_ID);
        }
        frozeRecord.setStatus(CustomerConstant.OPERATE_TYPE_F);
        frozeRecord.setProductId(req.getProductId());
        frozeRecord.setTenantId(req.getTenantId());
        frozeRecord.setMerchantNo(req.getMerchantNo());
        frozeRecord.setCustomerId(secondLevel.getCustomerId());
        frozeRecord.setFrozeNo(req.getFrozeNo());
        frozeRecord.setChannel(req.getChannel());
        frozeRecord.setRecordVersionNumber(CustomerConstant.INIT_RECORD_VERSION_NUMBER);
        frozeRecord.setLimitId(creditInfoRes.getLimitId());
        frozeRecord.setOrganizationId(organizationId);
        frozeRecord.setUserId(req.getUserId());
        frozeRecord.setUpdateBy(Constant.SYS_OPRATE);
        frozeRecord.setFrozeAmount(req.getFrozeAmount().setScale(2, BigDecimal.ROUND_HALF_UP));
        frozeRecord.setAvailableAmount(req.getFrozeAmount().setScale(2, BigDecimal.ROUND_HALF_UP));
        frozeRecord.setFrozeDesc(req.getAdjustReason());
        return frozeRecord;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CustomerLimitFrozeRes creditMedicalUnfreeze(CustomerLimitFrozeFrozeReq req) throws TxnException, InterruptedException {
        logger.info("creditMedicalUnfreeze|解冻处理|参数|req：{}", JSON.toJSONString(req));

        CustomerRes customerInfo = customerService.getCustomerByChannelAndUserId(req.getChannel(),
                req.getUserId(), req.getTenantId());
        CustomerSecondLevelRes secondLevel = customerInfo.getSecondLevel();

        //返回信息
        CustomerLimitFrozeRes frozeRes = new CustomerLimitFrozeRes();
        frozeRes.setChannel(req.getChannel());
        frozeRes.setProductId(req.getProductId());
        frozeRes.setSeqNo(req.getSeqNo());
        frozeRes.setFrozeNo(req.getFrozeNo());
        frozeRes.setUserId(req.getUserId());
        ;

        String lockSeqKey = req.getProductId() + secondLevel.getCustomerId() + req.getSeqNo();
        RLock lockSeq = redissonLockService.getRLock(lockSeqKey, LockType.REENTRANT_LOCK);

        ValueOperations<String, Object> redis = template.opsForValue();
        Boolean ifAbsent = redis.setIfAbsent(lockSeqKey, req.getSeqNo(), CustomerConstant.LOAN_REDIS_LOCK_LESS_TIME, TimeUnit.SECONDS);
        if (!ifAbsent) {
            String message = MessageFormat.format("存在相同流水号的请求，且正在处理中，流水号seqNo：{0}", req.getSeqNo());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.DATA_EXISTS.getCode(), message);
        }
        boolean lockSeqFlag = lockSeq.tryLock(CustomerConstant.LOAN_REDIS_LOCK_WAITE_TIME, CustomerConstant.LOAN_REDIS_LOCK_LESS_TIME, TimeUnit.SECONDS);
        if (!lockSeqFlag) {
            String msg = String.format(Constant.GET_REDIS_LOCK_ERROR, lockSeqKey);
            throw new TxnBizException(TxnRespCode.ERROR.getCode(), msg);
        }
        //3.查询渠道信息判断渠道是否存在
        logger.debug("1.查询渠道信息判断渠道是否存在,渠道：{}", req.getChannel());
        PrChannelTable channel = channelTableService.getChannelTableByChannelIdAndStatus(Constant.YES_FLAG,
                req.getChannel(), req.getTenantId());
        if (channel == null) {
            logger.error("渠道信息不存在");
            throw new TxnBizException(TxnRespCode.NO_DATA);
        }
        //获取额度冻结信息
        CmCustomerLimitFroze record = getCmCustomerLimitFroze(req.getTenantId(), secondLevel.getCustomerId(), req.getProductId(), CustomerConstant.OPERATE_TYPE_F, req.getFrozeNo());
        List<CmCustomerLimitFroze> cmCustomerFrozeLimits = this.limitFrozeQuery(record);
        if (CollectionUtils.isEmpty(cmCustomerFrozeLimits)) {
            frozeRes.setMsg("该用户没有需要解冻的数据");
            frozeRes.setResult(CustomerConstant.SUCCESS);
            return frozeRes;
        }

        //遍历查到的冻结信息，逐条解冻
        for (int i = 0; i < cmCustomerFrozeLimits.size(); i++) {
            CmCustomerLimitFroze toUnfreeze = cmCustomerFrozeLimits.get(i);
            String lockKey = req.getProductId() + secondLevel.getCustomerId() + toUnfreeze.getFrozeNo();
            RLock lock = redissonLockService.getRLock(lockKey, LockType.REENTRANT_LOCK);
            try {
                boolean lockFlag = lock.tryLock(CustomerConstant.LOAN_REDIS_LOCK_WAITE_TIME, CustomerConstant.LOAN_REDIS_LOCK_LESS_TIME, TimeUnit.SECONDS);
                if (!lockFlag) {
                    String msg = String.format(Constant.GET_REDIS_LOCK_ERROR, lockKey);
                    throw new TxnBizException(TxnRespCode.ERROR.getCode(), msg);
                }
                CmCustomerLimitFroze limitUnfreeze = generateUnFreezeParams(req, toUnfreeze, secondLevel);

                //额度冻结处理落地
                this.creditMedicalUpdateByParams(limitUnfreeze);
            } finally {
                if (lock.isLocked()) {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
        }

        //解冻处理结果返回
        frozeRes.setStatus(CustomerConstant.OPERATE_TYPE_R);
        frozeRes.setResult(CustomerConstant.SUCCESS);

        logger.info("creditMedicalFreeze|解冻处理|结果|res：{}", JSON.toJSONString(frozeRes));

        return frozeRes;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CustomerLimitFrozeUseRes creditMedicalUseFrozeLimit(CustomerUseLimitFrozeFrozeReq useLimitFrozeReq) throws TxnException, InterruptedException {
        logger.info("creditMedicalUseFrozeLimit|开始|参数：{}", JSON.toJSONString(useLimitFrozeReq));
        if (StringUtils.isBlank(useLimitFrozeReq.getFrozeNo())) {
            String message = MessageFormat.format("用户号：{0},产品号：{1},冻结流水号：{2},冻结额度的占用、恢复，冻结流水号不能为空",
                    useLimitFrozeReq.getUserId(), useLimitFrozeReq.getProductId(), useLimitFrozeReq.getFrozeNo());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }

        CustomerRes customerInfo = customerService.getCustomerByChannelAndUserId(useLimitFrozeReq.getChannel(),
                useLimitFrozeReq.getUserId(), useLimitFrozeReq.getTenantId());
        CustomerSecondLevelRes secondLevel = customerInfo.getSecondLevel();

        String lockKey = useLimitFrozeReq.getProductId() + secondLevel.getCustomerId() + useLimitFrozeReq.getFrozeNo();
        RLock lock = redissonLockService.getRLock(lockKey, LockType.REENTRANT_LOCK);

        CustomerLimitFrozeUseRes res = new CustomerLimitFrozeUseRes();
        try {
            boolean lockFlag = lock.tryLock(CustomerConstant.LOAN_REDIS_LOCK_WAITE_TIME, CustomerConstant.LOAN_REDIS_LOCK_LESS_TIME, TimeUnit.SECONDS);
            if (!lockFlag) {
                String msg = String.format(Constant.GET_REDIS_LOCK_ERROR, lockKey);
                throw new TxnBizException(TxnRespCode.ERROR.getCode(), msg);
            }
            //获取额度冻结信息
            CmCustomerLimitFroze record = getCmCustomerLimitFroze(useLimitFrozeReq.getTenantId(), secondLevel.getCustomerId(), useLimitFrozeReq.getProductId(), CustomerConstant.OPERATE_TYPE_F, useLimitFrozeReq.getFrozeNo());
            List<CmCustomerLimitFroze> cmCustomerFrozeLimits = this.limitFrozeQuery(record);
            if (CollectionUtils.isEmpty(cmCustomerFrozeLimits) || cmCustomerFrozeLimits.size() > 1) {
                String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},冻结额度信息不唯一",
                        secondLevel.getCustomerId(), useLimitFrozeReq.getProductId(), useLimitFrozeReq.getFrozeNo());
                logger.error(message);
                throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
            }
            //冻结流水号对应的额度信息
            CmCustomerLimitFroze cmCustomerLimitFroze = cmCustomerFrozeLimits.get(0);
            CmLimitFrozeUseLog frozeUseLog = cmLimitFrozeUseLogService.getLimitFrozeUseLogByUniqueKey(useLimitFrozeReq.getTenantId(), secondLevel.getCustomerId(),
                    useLimitFrozeReq.getChannel(), useLimitFrozeReq.getFrozeNo(), useLimitFrozeReq.getSerialNo(),Constant.USE_RESUME_FLAG_U);

            if (Constant.USE_RESUME_FLAG_R.equals(useLimitFrozeReq.getUseResumeFlag())){
                checkFrozeLimitResume(useLimitFrozeReq, secondLevel, frozeUseLog, cmCustomerLimitFroze);
                //恢复的额度必须和支付流水号冻结的额度相等
                if (useLimitFrozeReq.getUseAmount() == null){
                    useLimitFrozeReq.setUseAmount(frozeUseLog.getUsedAmount());
                }
            } else {
                checkFrozeLimitUse(useLimitFrozeReq, secondLevel, frozeUseLog, cmCustomerLimitFroze);
            }

            CmCustomerLimitFroze useCustomerLimit = generateLimitUse(useLimitFrozeReq, cmCustomerLimitFroze);
            frozeUseLog = getCmLimitFrozeUseLog(useLimitFrozeReq, secondLevel, cmCustomerLimitFroze);
            cmLimitFrozeUseLogService.insert(frozeUseLog);
            //冻结流水号下的额度使用信息落地
            this.creditMedicalUpdateByParams(useCustomerLimit);
            res.setStatus(cmCustomerLimitFroze.getStatus());
        } finally {
            if (lock.isLocked()) {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
        res.setFrozeNo(useLimitFrozeReq.getFrozeNo());
        res.setUserId(useLimitFrozeReq.getUserId());
        res.setSeqNo(useLimitFrozeReq.getSeqNo());
        res.setChannel(useLimitFrozeReq.getChannel());
        res.setResult(CustomerConstant.SUCCESS);
        res.setFrozeNo(res.getFrozeNo());
        res.setProductId(useLimitFrozeReq.getProductId());
        res.setSerialNo(useLimitFrozeReq.getSerialNo());
        logger.info("creditMedicalUseFrozeLimit|结束|反回数据：{}", JSON.toJSONString(res));
        return res;
    }

    private void checkFrozeLimitUse(CustomerUseLimitFrozeFrozeReq useLimitFrozeReq, CustomerSecondLevelRes secondLevel, CmLimitFrozeUseLog frozeUseLog,CmCustomerLimitFroze cmCustomerLimitFroze) throws TxnBizException {
        if (frozeUseLog != null) {
            String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},支付流水号：{3}，支用记录重复",
                    secondLevel.getCustomerId(), useLimitFrozeReq.getProductId(), useLimitFrozeReq.getFrozeNo(), useLimitFrozeReq.getSerialNo());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
        //冻结状态校验
        if (!CustomerConstant.OPERATE_TYPE_F.equals(cmCustomerLimitFroze.getStatus())) {
            String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},冻结状态：{3}，只有在额度冻结的场景下才能使用对应的额度",
                    secondLevel.getCustomerId(), useLimitFrozeReq.getProductId(), useLimitFrozeReq.getFrozeNo(), cmCustomerLimitFroze.getStatus());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
        //冻结流水号对应的可用额度检查
        if (useLimitFrozeReq.getUseAmount().compareTo(cmCustomerLimitFroze.getAvailableAmount()) > 0) {
            String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},可用额度：{3},使用额度：{4}，使用额度大于可用额度",
                    secondLevel.getCustomerId(), useLimitFrozeReq.getProductId(), useLimitFrozeReq.getFrozeNo(), cmCustomerLimitFroze.getAvailableAmount(), useLimitFrozeReq.getUseAmount());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
    }

    private void checkFrozeLimitResume(CustomerUseLimitFrozeFrozeReq useLimitFrozeReq, CustomerSecondLevelRes secondLevel, CmLimitFrozeUseLog frozeUseLog,CmCustomerLimitFroze cmCustomerLimitFroze) throws TxnBizException {
        if (frozeUseLog == null) {
            String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},支付流水号：{3}，没有找到对应的额度占用记录",
                    secondLevel.getCustomerId(), useLimitFrozeReq.getProductId(), useLimitFrozeReq.getFrozeNo(), useLimitFrozeReq.getSerialNo());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
        CmLimitFrozeUseLog frozeUseLogResume = cmLimitFrozeUseLogService.getLimitFrozeUseLogByUniqueKey(useLimitFrozeReq.getTenantId(), secondLevel.getCustomerId(),
                useLimitFrozeReq.getChannel(), useLimitFrozeReq.getFrozeNo(), useLimitFrozeReq.getSerialNo(),Constant.USE_RESUME_FLAG_R);
        if (frozeUseLogResume != null) {
            String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},支付流水号：{3}，已经做过了额度恢复，无需重复操作",
                    secondLevel.getCustomerId(), useLimitFrozeReq.getProductId(), useLimitFrozeReq.getFrozeNo(), useLimitFrozeReq.getSerialNo());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
        //冻结状态校验
        if (!CustomerConstant.OPERATE_TYPE_F.equals(cmCustomerLimitFroze.getStatus())) {
            String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},冻结状态：{3}，只有在额度冻结的场景下才能恢复对应的额度",
                    secondLevel.getCustomerId(), useLimitFrozeReq.getProductId(), useLimitFrozeReq.getFrozeNo(), cmCustomerLimitFroze.getStatus());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
        //冻结流水号对应的可用额度检查
        if (useLimitFrozeReq.getUseAmount() != null && useLimitFrozeReq.getUseAmount().compareTo(frozeUseLog.getUsedAmount()) != 0) {
            String message = MessageFormat.format("客户号：{0},产品号：{1},冻结流水号：{2},支付流水号：{3}，对应的占用额度和恢复额度不等",
                    secondLevel.getCustomerId(), useLimitFrozeReq.getProductId(), useLimitFrozeReq.getFrozeNo(), useLimitFrozeReq.getSerialNo());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
    }

    private CmLimitFrozeUseLog getCmLimitFrozeUseLog(CustomerUseLimitFrozeFrozeReq useLimitFrozeReq, CustomerSecondLevelRes secondLevel, CmCustomerLimitFroze cmCustomerLimitFroze) throws TxnException {
        CmLimitFrozeUseLog frozeUseLog;
        frozeUseLog = new CmLimitFrozeUseLog();
        try {
            frozeUseLog.setId(segmentService.getId(Constant.CM_LIMIT_FROZE_USE_LOG));
        } catch (SegmentException e) {
            logger.error("主键生成错误", e);
            throw new TxnException(com.jrx.anytxn.param.constant.ErrorMsgConstant.ERROR_ID);
        }
        frozeUseLog.setUserId(cmCustomerLimitFroze.getUserId());
        frozeUseLog.setFrozeNo(useLimitFrozeReq.getFrozeNo());
        frozeUseLog.setChannel(useLimitFrozeReq.getChannel());
        frozeUseLog.setCustomerId(secondLevel.getCustomerId());
        frozeUseLog.setPaymentNo(useLimitFrozeReq.getSerialNo());
        frozeUseLog.setUseResumeFlag(useLimitFrozeReq.getUseResumeFlag());
        frozeUseLog.setMerchantNo(useLimitFrozeReq.getMerchantNo());
        frozeUseLog.setOrganizationId(cmCustomerLimitFroze.getOrganizationId());
        frozeUseLog.setProductId(useLimitFrozeReq.getProductId());
        frozeUseLog.setTenantId(useLimitFrozeReq.getTenantId());
        frozeUseLog.setUsedAmount(useLimitFrozeReq.getUseAmount());
        frozeUseLog.setUpdateBy(Constant.SYS_OPRATE);
        frozeUseLog.setRecordVersionNumber(Constant.VERSION_NUMBER);
        return frozeUseLog;
    }

    private CmCustomerLimitFroze generateLimitUse(CustomerUseLimitFrozeFrozeReq useLimitFrozeReq, CmCustomerLimitFroze cmCustomerLimitFroze) {
        CmCustomerLimitFroze updateLimit = new CmCustomerLimitFroze();
        updateLimit.setUpdateBy(Constant.SYS_OPRATE);
        updateLimit.setFrozeNo(cmCustomerLimitFroze.getFrozeNo());
        if (Constant.USE_RESUME_FLAG_R.equals(useLimitFrozeReq.getUseResumeFlag())){
            updateLimit.setUsedAmount(cmCustomerLimitFroze.getUsedAmount().subtract(useLimitFrozeReq.getUseAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
            updateLimit.setAvailableAmount(cmCustomerLimitFroze.getAvailableAmount().add(useLimitFrozeReq.getUseAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
        } else {
            updateLimit.setUsedAmount(cmCustomerLimitFroze.getUsedAmount().add(useLimitFrozeReq.getUseAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
            updateLimit.setAvailableAmount(cmCustomerLimitFroze.getAvailableAmount().subtract(useLimitFrozeReq.getUseAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
        }
        updateLimit.setRecordVersionNumber(cmCustomerLimitFroze.getRecordVersionNumber());
        updateLimit.setTenantId(useLimitFrozeReq.getTenantId());
        updateLimit.setProductId(useLimitFrozeReq.getProductId());
        updateLimit.setCustomerId(cmCustomerLimitFroze.getCustomerId());
        updateLimit.setChannel(useLimitFrozeReq.getChannel());
        return updateLimit;
    }

    private CmCustomerLimitFroze generateUnFreezeParams(CustomerLimitFrozeFrozeReq req, CmCustomerLimitFroze toUnfreeze, CustomerSecondLevelRes secondLevel) {
        CmCustomerLimitFroze frozeRecord = new CmCustomerLimitFroze();

        frozeRecord.setStatus(CustomerConstant.OPERATE_TYPE_R);
        frozeRecord.setProductId(req.getProductId());
        frozeRecord.setTenantId(req.getTenantId());
        frozeRecord.setCustomerId(secondLevel.getCustomerId());
        frozeRecord.setUserId(req.getUserId());
        frozeRecord.setFrozeNo(toUnfreeze.getFrozeNo());
        frozeRecord.setChannel(req.getChannel());
        frozeRecord.setUnfrozeDesc(req.getAdjustReason());
        frozeRecord.setUpdateBy(Constant.SYS_OPRATE);
//        frozeRecord.setFrozeAmount(BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP));
        frozeRecord.setUnfrozeTime(new Date());
        frozeRecord.setAvailableAmount(BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP));
        frozeRecord.setUnfrozeDesc(req.getAdjustReason());
        frozeRecord.setRecordVersionNumber(toUnfreeze.getRecordVersionNumber());
        return frozeRecord;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public CustomerLimitFrozeBaseRes limitFreezeResultQuery(CustomerLimitFrozeResultReq queryReq) throws TxnException {
        logger.info("limitFreezeResultQuery|查询参数| queryReq：{}", JSON.toJSONString(queryReq));
        CustomerLimitFrozeBaseRes res = new CustomerLimitFrozeBaseRes();
        res.setUserId(queryReq.getUserId());
        res.setSeqNo(queryReq.getSeqNo());
        res.setChannel(queryReq.getChannel());
        res.setProductId(queryReq.getProductId());
        res.setTenantId(queryReq.getTenantId());

        CustomerRes customerInfo = customerService.getCustomerByChannelAndUserId(queryReq.getChannel(),
                queryReq.getUserId(), queryReq.getTenantId());
        CustomerSecondLevelRes secondLevel = customerInfo.getSecondLevel();
        //获取额度冻结信息
        CmCustomerLimitFroze params = getCmCustomerLimitFroze(queryReq.getTenantId(), secondLevel.getCustomerId(), queryReq.getProductId(), queryReq.getOperateType(), queryReq.getFrozeNo());
        List<CmCustomerLimitFroze> cmCustomerFrozeLimits = this.limitFrozeQuery(params);
        if (CollectionUtils.isEmpty(cmCustomerFrozeLimits)) {
            res.setMsg("没有找到对应的冻结或解冻结果");
        }
        List<CustomerLimitFrozeDetailResp> list = new ArrayList<>();
        cmCustomerFrozeLimits.forEach(cmCustomerLimitFroze -> {
            CustomerLimitFrozeDetailResp resp = getCustomerLimitFrozeDetailResp(cmCustomerLimitFroze);
            list.add(resp);
        });
        res.setDetails(list);
        logger.info("limitFreezeResultQuery|查询结果| res：{}", JSON.toJSONString(res));
        return res;
    }

    private CustomerLimitFrozeDetailResp getCustomerLimitFrozeDetailResp(CmCustomerLimitFroze cmCustomerLimitFroze) {
        Assert.notNull(cmCustomerLimitFroze, "参数不能为空");
        CustomerLimitFrozeDetailResp resp = new CustomerLimitFrozeDetailResp();
        resp.setFrozeNo(cmCustomerLimitFroze.getFrozeNo());
        resp.setChannel(cmCustomerLimitFroze.getChannel());
        resp.setStatus(cmCustomerLimitFroze.getStatus());
        resp.setProductId(cmCustomerLimitFroze.getProductId());
        resp.setUserId(cmCustomerLimitFroze.getUserId());
        resp.setFrozeLimit(cmCustomerLimitFroze.getFrozeAmount());
        resp.setFrozeAvailableLimit(cmCustomerLimitFroze.getAvailableAmount());
        resp.setResult(CustomerConstant.SUCCESS);
        return resp;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CustomerLimitFrozeQueryRes limitFreezeInfoQuery(CustomerLimitFrozeInfoReq queryReq) throws TxnException {

        CustomerRes customerInfo = customerService.getCustomerByChannelAndUserId(queryReq.getChannel(),
                queryReq.getUserId(), queryReq.getTenantId());
        CustomerSecondLevelRes secondLevel = customerInfo.getSecondLevel();
        //获取客户-产品对应的唯一额度信息
        CreditInfoRes creditLimitInfo = getCustomerCreditLimitInfo(queryReq.getProductId(), queryReq.getSeqNo(), queryReq.getUserId(), secondLevel.getCustomerId(), queryReq.getChannel(), queryReq.getTenantId());
        //获取额度冻结信息
        CmCustomerLimitFroze record = getCmCustomerLimitFroze(queryReq.getTenantId(), secondLevel.getCustomerId(), queryReq.getProductId(), CustomerConstant.OPERATE_TYPE_F, queryReq.getFrozeNo());
        List<CmCustomerLimitFroze> cmCustomerFrozeLimits = this.limitFrozeQuery(record);
        //返回信息
        CustomerLimitFrozeQueryRes frozeQueryRes = new CustomerLimitFrozeQueryRes();
        frozeQueryRes.setSeqNo(queryReq.getSeqNo());
        frozeQueryRes.setLimitAmt(creditLimitInfo.getCreditLimit());
        frozeQueryRes.setAvailableLimit(creditLimitInfo.getAvailCreditLimit());
        frozeQueryRes.setChannel(queryReq.getChannel());
        frozeQueryRes.setProductId(queryReq.getProductId());
        frozeQueryRes.setUserId(queryReq.getUserId());
        frozeQueryRes.setTenantId(queryReq.getTenantId());
        frozeQueryRes.setEffectiveDate(creditLimitInfo.getEffectiveDate());
        frozeQueryRes.setExpiryDate(creditLimitInfo.getExpiryDate());
        frozeQueryRes.setLimitStatus(creditLimitInfo.getLimitStatus());
        if (CollectionUtils.isNotEmpty(cmCustomerFrozeLimits)) {
            List<CustomerLimitFrozeDetailResp> details = cmCustomerFrozeLimits.stream().map(this::getCustomerLimitFrozeDetailResp).collect(Collectors.toList());
            frozeQueryRes.setDetails(details);
        } else {
            frozeQueryRes.setMsg("没有找到对应类型是额度冻结信息");
        }
        return frozeQueryRes;
    }

    private CmCustomerLimitFroze getCmCustomerLimitFroze(String tenantId, String customerId, String productId, String operateType, String frozeNo) {
        CmCustomerLimitFroze record = new CmCustomerLimitFroze();
        record.setTenantId(tenantId);
        record.setCustomerId(customerId);
        record.setProductId(productId);
        record.setStatus(operateType);
        record.setFrozeNo(frozeNo);
        return record;
    }

    private CreditInfoRes getCustomerCreditLimitInfo(String productId, String seqNO, String userId, String customerId, String channel, String tenantId) throws TxnException {
        CreditInfoQueryReq creditInfoQueryReq = new CreditInfoQueryReq();
        creditInfoQueryReq.setProductId(productId);
        creditInfoQueryReq.setSeqNo(seqNO);
        creditInfoQueryReq.setUserId(userId);
        creditInfoQueryReq.setChannel(channel);
        creditInfoQueryReq.setTenantId(tenantId);
        CreditInfoQueryRes creditApproval = customerLimitInfoHandler.getCreditApproval(creditInfoQueryReq);
        if (creditApproval == null || CollectionUtils.isEmpty(creditApproval.getCreditInfoList())) {
            String message = MessageFormat.format("userId:{0},客户号：{1},产品号：{2},额度信息不存在",
                    creditInfoQueryReq.getUserId(), customerId, creditInfoQueryReq.getProductId());
            logger.error(message);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
        }
        CreditInfoRes creditInfoRes = creditApproval.getCreditInfoList().get(0);
        return creditInfoRes;
    }

}
